﻿using Calculation;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;

namespace VS11MultiLang
{

    class RunnerViewModel : ViewModel
    {
        const int SIZE = 1000000;

        double currentCppResult;
        double currentCSharpResult;

        private bool isRunning = false;

        CppRunner.Runner cppRunner = new CppRunner.Runner(SIZE);
        CSharpRunner.Runner csharpRunner = new CSharpRunner.Runner(SIZE);

        CalculationProgress cppProgress = new CalculationProgress();
        CalculationProgress csProgress = new CalculationProgress();

        private ObservableDataSource<Point> cppHistory = new ObservableDataSource<Point>();
        private ObservableDataSource<Point> csHistory = new ObservableDataSource<Point>();


        private string buttonLabel_ = "Run";

        public RunnerViewModel()
        {

        }

        public string ButtonLabel
        {
            get { return buttonLabel_; }
            set
            {
                if (value != buttonLabel_)
                {
                    buttonLabel_ = value;
                    RaisePropertyChanged("ButtonLabel");
                }
            }
        }

        public ICommand RunCommand
        {
            get { return new RelayCommand(Run); }
        }

        void Run(object param)
        {
            if (isRunning)
            {
                StopCalculation();
            }
            else
            {
                StartCalculation();
            }
        }

        private void StartCalculation()
        {
            isRunning = true;

            RaisePropertyChanged("RunCommand");
            StartCSharp();
            StartCpp();

            ButtonLabel = "Cancel";
        }

        private void StopCalculation()
        {
            csProgress.CancellationPending = true;
            cppProgress.CancellationPending = true;
            isRunning = false;
            ButtonLabel = "Run";
        }

        public ObservableDataSource<Point> CppHistory
        {
            get { return cppHistory; }
        }

        public ObservableDataSource<Point> CsHistory
        {
            get { return csHistory; }
        }

        void StartCpp()
        {
            BackgroundWorker cpp = new BackgroundWorker();
            
            cppProgress.ProgressChanged += ShowCppProgress;
            cpp.DoWork += (sender, e) => { cppRunner.Run(cppProgress); };
            cpp.RunWorkerCompleted += (sender, e) =>
            {
                currentCppResult = cppRunner.Result;
                isRunning = false;
                RaisePropertyChanged("");
            };
            cpp.RunWorkerAsync();
        }

        void StartCSharp()
        {
            BackgroundWorker csharp = new BackgroundWorker();
            csProgress.ProgressChanged += ShowCSharpProgress;
            csharp.DoWork += (sender, e) => { csharpRunner.Run(csProgress); };
            csharp.RunWorkerCompleted += (sender, e) =>
            {
                currentCSharpResult = csharpRunner.Result;
                isRunning = false;
                RaisePropertyChanged("");
            };

            csharp.RunWorkerAsync();
        }

        void ShowCppProgress(CalculationProgress progress)
        {
            if (Double.IsInfinity(progress.CurrentResult) ||
                Double.IsNaN(progress.CurrentResult))
                return;
            var dispatcher = Application.Current.Dispatcher;
            dispatcher.Invoke(() =>
            {
                cppHistory.Collection.Add(new Point(progress.Samples, progress.CurrentResult));
                currentCppResult = progress.CurrentResult;
                RaisePropertyChanged("CppResult");
            });
        }

        void ShowCSharpProgress(CalculationProgress progress)
        {
            if (Double.IsInfinity(progress.CurrentResult) ||
                Double.IsNaN(progress.CurrentResult))
                return;
            var dispatcher = Application.Current.Dispatcher;
            dispatcher.Invoke(() =>
                {
                    var step = csHistory.Collection.Count;
                    csHistory.Collection.Add(new Point(progress.Samples, progress.CurrentResult));
                    currentCSharpResult = progress.CurrentResult;
                    RaisePropertyChanged("CSharpResult");
                });
        }

        bool IsReadyToRun(object param)
        {
            return isRunning;
        }

        public string CppResult
        {
            get { return currentCppResult.ToString(); }
        }

        public string CSharpResult
        {
            get { return currentCSharpResult.ToString(); }
        }
    }
}
